home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / DB / QueryTool / Query.php < prev    next >
PHP Script  |  2004-10-01  |  81KB  |  2,384 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP Version 4                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1997-2003 The PHP Group                                |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2.02 of the PHP license,      |
  8. // | that is bundled with this package in the file LICENSE, and is        |
  9. // | available at through the world-wide-web at                           |
  10. // | http://www.php.net/license/2_02.txt.                                 |
  11. // | If you did not receive a copy of the PHP license and are unable to   |
  12. // | obtain it through the world-wide-web, please send a note to          |
  13. // | license@php.net so we can mail you a copy immediately.               |
  14. // +----------------------------------------------------------------------+
  15. // | Author:  Wolfram Kriesing, Paolo Panto, vision:produktion <wk@visionp.de>
  16. // +----------------------------------------------------------------------+
  17. //
  18. // $Id: Query.php,v 1.47 2004/05/02 15:34:52 quipo Exp $
  19. //
  20.  
  21. require_once 'PEAR.php';
  22. require_once 'DB.php';
  23.  
  24.  
  25. /**
  26.  * this class should be extended
  27.  *
  28.  * @package    DB_QueryTool
  29.  * @version    2002/04/02
  30.  * @access     public
  31.  * @author     Wolfram Kriesing <wk@visionp.de>
  32.  */
  33. class DB_QueryTool_Query
  34. {
  35.     // {{{ class vars
  36.  
  37.     /**
  38.      * @var string  the name of the primary column
  39.      */
  40.     var $primaryCol = 'id';
  41.  
  42.     /**
  43.      * @var string  the current table the class works on
  44.      */
  45.     var $table      = '';
  46.  
  47.     /**
  48.      * @var string  the name of the sequence for this table
  49.      */
  50.     var $sequenceName = null;
  51.  
  52.     /**
  53.      * @var object  the db-object, a PEAR::Db-object instance
  54.      */
  55.     var $db = null;
  56.  
  57.     /**
  58.      * @var string  the where condition
  59.      * @access private
  60.      */
  61.     var $_where = '';
  62.  
  63.     /**
  64.      * @var string  the order condition
  65.      * @access private
  66.      */
  67.     var $_order = '';
  68.  
  69.     /**
  70.      * @var    string  the having definition
  71.      * @access private
  72.      */
  73.     var $_having = '';
  74.  
  75.     /**
  76.      * @var array   contains the join content
  77.      *              the key is the join type, for now we have 'default' and 'left'
  78.      *              inside each key 'table' contains the table
  79.      *                          key 'where' contains the where clause for the join
  80.      * @access private
  81.      */
  82.     var $_join = array();
  83.  
  84.     /**
  85.      * @var    string  which column to index the result by
  86.      * @access private
  87.      */
  88.     var $_index = null;
  89.  
  90.     /**
  91.      * @var    string  the group-by clause
  92.      * @access private
  93.      */
  94.     var $_group = '';
  95.  
  96.     /**
  97.      * @var    array   the limit
  98.      * @access private
  99.      */
  100.     var $_limit = array();
  101.  
  102.     /**
  103.      * @var    string  type of result to return
  104.      * @access private
  105.      */
  106.     var $_resultType = 'none';
  107.  
  108.     /**
  109.      * @var    array   the metadata temporary saved
  110.      * @access private
  111.      */
  112.     var $_metadata = array();
  113.  
  114.     /**
  115.      * @var    string
  116.      * @access private
  117.      */
  118.     var $_lastQuery = null;
  119.  
  120.     /**
  121.      * @var    string   the rows that shall be selected
  122.      * @access private
  123.      */
  124.     var $_select = '*';
  125.  
  126.     /**
  127.      * @var    string   the rows that shall not be selected
  128.      * @access private
  129.      */
  130.     var $_dontSelect = '';
  131.  
  132.     /**
  133.      * @var array  this array saves different modes in which this class works
  134.      *             i.e. 'raw' means no quoting before saving/updating data
  135.      * @access private
  136.      */
  137.     var $options = array(
  138.         'raw'      =>  false,
  139.         'verbose'  =>  true,    // set this to false in a productive environment
  140.                                 // it will produce error-logs if set to true
  141.         'useCache' =>  false,
  142.         'logFile'  =>  false,
  143.     );
  144.  
  145.     /**
  146.      * this array contains information about the tables
  147.      * those are
  148.      * - 'name' => the real table name
  149.      * - 'shortName' => the short name used, so that when moving the table i.e.
  150.      *                  onto a provider's db and u have to rename the tables to
  151.      *                  longer names this name will be relevant, i.e. when
  152.      *                  autoJoining, i.e. a table name on your local machine is:
  153.      *                  'user' but online it has to be 'applName_user' then the
  154.      *                  shortName will be used to determine if a column refers to
  155.      *                  another table, if the colName is 'user_id', it knows the
  156.      *                  shortName 'user' refers to the table 'applName_user'
  157.      */
  158.     var $tableSpec = array();
  159.  
  160.     /**
  161.      * this is the regular expression that shall be used to find a table's shortName
  162.      * in a column name, the string found by using this regular expression will be removed
  163.      * from the column name and it will be checked if it is a table name
  164.      * i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id'
  165.      */
  166.     var $_tableNameToShortNamePreg = '/^.*_/';
  167.  
  168.     /**
  169.      * @var array this array caches queries that have already been built once
  170.      *            to reduce the execution time
  171.      */
  172.     var $_queryCache = array();
  173.  
  174.     /**
  175.      * The object that contains the log-instance
  176.      */
  177.     var $_logObject = null;
  178.  
  179.     /**
  180.      * Some internal data the logging needs
  181.      */
  182.     var $_logData = array();
  183.  
  184.     // }}}
  185.     // {{{ __construct()
  186.  
  187.     /**
  188.      * this is the constructor, as it will be implemented in ZE2 (php5)
  189.      *
  190.      * @version    2002/04/02
  191.      * @access     public
  192.      * @author     Wolfram Kriesing <wk@visionp.de>
  193.      * @param      object  db-object
  194.      */
  195. /*
  196.     function __construct($dsn=false, $options=array())
  197.     {
  198.         if (!isset($options['autoConnect'])) {
  199.             $autoConnect = true;
  200.         } else {
  201.             $autoConnect = $options['autoConnect'];
  202.         }
  203.         if (isset($options['errorCallback'])) {
  204.             $this->setErrorCallback($options['errorCallback']);
  205.         }
  206.         if (isset($options['errorSetCallback'])) {
  207.             $this->setErrorSetCallback($options['errorSetCallback']);
  208.         }
  209.         if (isset($options['errorLogCallback'])) {
  210.             $this->setErrorLogCallback($options['errorLogCallback']);
  211.         }
  212.  
  213.         if ($autoConnect && $dsn) {
  214.             $this->connect($dsn, $options);
  215.         }
  216.         //we would need to parse the dsn first ... i dont feel like now :-)
  217.         // oracle has all column names in upper case
  218. //FIXXXME make the class work only with upper case when we work with oracle
  219.         //if ($this->db->phptype=='oci8' && !$this->primaryCol) {
  220.         //    $this->primaryCol = 'ID';
  221.         //}
  222.  
  223.         if ($this->sequenceName == null) {
  224.             $this->sequenceName = $this->table;
  225.         }
  226.     }
  227. */
  228.  
  229.     // }}}
  230.     // {{{ DB_QueryTool_Query()
  231.  
  232.     /**
  233.      * @version    2002/04/02
  234.      * @access     public
  235.      * @author     Wolfram Kriesing <wk@visionp.de>
  236.      * @param mixed $dsn DSN string, DSN array or DB object
  237.      * @param array $options
  238.      */
  239.     function DB_QueryTool_Query($dsn=false, $options=array())
  240.     {
  241.         //$this->__construct($dsn, $options);
  242.         if (!isset($options['autoConnect'])) {
  243.             $autoConnect = true;
  244.         } else {
  245.             $autoConnect = $options['autoConnect'];
  246.         }
  247.         if (isset($options['errorCallback'])) {
  248.             $this->setErrorCallback($options['errorCallback']);
  249.         }
  250.         if (isset($options['errorSetCallback'])) {
  251.             $this->setErrorSetCallback($options['errorSetCallback']);
  252.         }
  253.         if (isset($options['errorLogCallback'])) {
  254.             $this->setErrorLogCallback($options['errorLogCallback']);
  255.         }
  256.         if ($autoConnect && $dsn) {
  257.             $this->connect($dsn, $options);
  258.         }
  259.         if (is_null($this->sequenceName)) {
  260.             $this->sequenceName = $this->table;
  261.         }
  262.     }
  263.  
  264.     // }}}
  265.     // {{{ connect()
  266.  
  267.     /**
  268.      * use this method if you want to connect manually
  269.      * @param mixed $dsn DSN string, DSN array or MDB object
  270.      * @param array $options
  271.      */
  272.     function connect($dsn, $options=array())
  273.     {
  274.         $res = $this->db = DB::connect($dsn, $options);
  275.         if (DB::isError($res)) {
  276. // FIXXME what shall we do here?
  277.             $this->_errorLog($res->getUserInfo());
  278.         } else {
  279.             $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  280.         }
  281.     }
  282.  
  283.     // }}}
  284.     // {{{ getDbInstance()
  285.  
  286.     /**
  287.      * @return reference to current DB instance
  288.      */
  289.     function &getDbInstance()
  290.     {
  291.         return $this->db;
  292.     }
  293.  
  294.     // }}}
  295.     // {{{ setDbInstance()
  296.  
  297.     /**
  298.      * Setup using an existing connection.
  299.      * this also sets the DB_FETCHMODE_ASSOC since this class
  300.      * needs this to be set!
  301.      *
  302.      * @param object a reference to an existing DB-object
  303.      * @return void
  304.      */
  305.     function setDbInstance(&$dbh)
  306.     {
  307.         $this->db =& $dbh;
  308.         $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  309.     }
  310.  
  311.     // }}}
  312.     // {{{ get()
  313.  
  314.     /**
  315.      * get the data of a single entry
  316.      * if the second parameter is only one column the result will be returned
  317.      * directly not as an array!
  318.      *
  319.      * @version    2002/03/05
  320.      * @access     public
  321.      * @author     Wolfram Kriesing <wk@visionp.de>
  322.      * @param      integer the id of the element to retreive
  323.      * @param      string  if this is given only one row shall be returned, directly, not an array
  324.      * @return     mixed   (1) an array of the retreived data
  325.      *                     (2) if the second parameter is given and its only one column,
  326.      *                         only this column's data will be returned
  327.      *                     (3) false in case of failure
  328.      */
  329.     function get($id, $column='')
  330.     {
  331.         $table = $this->table;
  332.         $getMethod = 'getRow';
  333.         if ($column && !strpos($column, ',')) {   // if only one column shall be selected
  334.             $getMethod = 'getOne';
  335.         }
  336.         // we dont use 'setSelect' here, since this changes the setup of the class, we
  337.         // build the query directly
  338.         // if $column is '' then _buildSelect selects '*' anyway, so that's the same behaviour as before
  339.         $query['select'] = $this->_buildSelect($column);
  340.         $query['where']  = $this->_buildWhere($this->table.'.'.$this->primaryCol.'='.$id);
  341.         $queryString = $this->_buildSelectQuery($query);
  342.  
  343.         return $this->returnResult($this->execute($queryString,$getMethod));
  344.     }
  345.  
  346.     // }}}
  347.     // {{{ getMultiple()
  348.  
  349.     /**
  350.      * gets the data of the given ids
  351.      *
  352.      * @version    2002/04/23
  353.      * @access     public
  354.      * @author     Wolfram Kriesing <wk@visionp.de>
  355.      * @param      array   this is an array of ids to retreive
  356.      * @param      string  the column to search in for
  357.      * @return     mixed   an array of the retreived data, or false in case of failure
  358.      *                       when failing an error is set in $this->_error
  359.      */
  360.     function getMultiple($ids, $column='')
  361.     {
  362.         $col = $this->primaryCol;
  363.         if ($column) {
  364.             $col = $column;
  365.         }
  366. // FIXXME if $ids has no table.col syntax and we are using joins, the table better be put in front!!!
  367.         $ids = $this->_quoteArray($ids);
  368.  
  369.         $query['where'] = $this->_buildWhere($col.' IN ('.implode(',', $ids).')');
  370.         $queryString = $this->_buildSelectQuery($query);
  371.  
  372.         return $this->returnResult($this->execute($queryString));
  373.     }
  374.  
  375.     // }}}
  376.     // {{{ getAll()
  377.  
  378.     /**
  379.      * get all entries from the DB
  380.      * for sorting use setOrder!!!, the last 2 parameters are deprecated
  381.      *
  382.      * @version    2002/03/05
  383.      * @access     public
  384.      * @author     Wolfram Kriesing <wk@visionp.de>
  385.      * @param      int     to start from
  386.      * @param      int     the number of rows to show
  387.      * @param      string  the DB-method to use, i dont know if we should leave this param here ...
  388.      * @return     mixed   an array of the retreived data, or false in case of failure
  389.      *                       when failing an error is set in $this->_error
  390.      */
  391.     function getAll($from=0,$count=0,$method='getAll')
  392.     {
  393.         $query = array();
  394.         if ($count) {
  395.             $query = array('limit' => array($from, $count));
  396.         }
  397.         return $this->returnResult($this->execute($this->_buildSelectQuery($query), $method));
  398.     }
  399.  
  400.     // }}}
  401.     // {{{ getCol()
  402.  
  403.     /**
  404.      * this method only returns one column, so the result will be a one dimensional array
  405.      * this does also mean that using setSelect() should be set to *one* column, the one you want to
  406.      * have returned a most common use case for this could be:
  407.      *      $table->setSelect('id');
  408.      *      $ids = $table->getCol();
  409.      * OR
  410.      *      $ids = $table->getCol('id');
  411.      * so ids will ba an array with all the id's
  412.      *
  413.      * @version    2003/02/25
  414.      * @access     public
  415.      * @author     Wolfram Kriesing <wk@visionp.de>
  416.      * @param      string  the column that shall be retreived
  417.      * @param      int     to start from
  418.      * @param      int     the number of rows to show
  419.      * @return     mixed   an array of the retreived data, or false in case of failure
  420.      *                     when failing an error is set in $this->_error
  421.      */
  422.     function getCol($column=null, $from=0, $count=0)
  423.     {
  424.         $query = array();
  425.         if (!is_null($column)) {
  426.             // by using _buildSelect() i can be sure that the table name will not be ambigious
  427.             // i.e. in a join, where all the joined tables have a col 'id'
  428.             // _buildSelect() will put the proper table name in front in case there is none
  429.             $query['select'] = $this->_buildSelect($column);
  430.         }
  431.         if ($count) {
  432.             $query['limit'] = array($from,$count);
  433.         }
  434.         return $this->returnResult($this->execute($this->_buildSelectQuery($query), 'getCol'));
  435.     }
  436.  
  437.     // }}}
  438.     // {{{ getCount()
  439.  
  440.     /**
  441.      * get the number of entries
  442.      *
  443.      * @version    2002/04/02
  444.      * @access     public
  445.      * @author     Wolfram Kriesing <wk@visionp.de>
  446.      * @param
  447.      * @return     mixed   an array of the retreived data, or false in case of failure
  448.      *                       when failing an error is set in $this->_error
  449.      */
  450.     function getCount()
  451.     {
  452. /* the following query works on mysql
  453. SELECT count(DISTINCT image.id) FROM image2tree
  454. RIGHT JOIN image ON image.id = image2tree.image_id
  455. the reason why this is needed - i just wanted to get the number of rows that do exist if the result is grouped by image.id
  456. the following query is what i tried first, but that returns the number of rows that have been grouped together
  457. for each image.id
  458. SELECT count(*) FROM image2tree
  459. RIGHT JOIN image ON image.id = image2tree.image_id GROUP BY image.id
  460.  
  461. so that's why we do the following, i am not sure if that is standard SQL and absolutley correct!!!
  462. */
  463.  
  464. //FIXXME see comment above if this is absolutely correct!!!
  465.         if ($group = $this->_buildGroup()) {
  466.             $query['select'] = 'COUNT(DISTINCT '.$group.')';
  467.             $query['group'] = '';
  468.         } else {
  469.             $query['select'] = 'COUNT(*)';
  470.         }
  471.  
  472.         $query['order'] = '';   // order is not of importance and might freak up the special group-handling up there, since the order-col is not be known
  473. /*# FIXXME use the following line, but watch out, then it has to be used in every method, or this
  474. # value will be used always, simply try calling getCount and getAll afterwards, getAll will return the count :-)
  475. # if getAll doenst use setSelect!!!
  476. */
  477.         //$this->setSelect('count(*)');
  478.         $queryString = $this->_buildSelectQuery($query);
  479.  
  480.         return ($res = $this->execute($queryString, 'getOne')) ? $res : 0;
  481.     }
  482.  
  483.     // }}}
  484.     // {{{ getDefaultValues()
  485.  
  486.     /**
  487.      * return an empty element where all the array elements do already exist
  488.      * corresponding to the columns in the DB
  489.      *
  490.      * @version    2002/04/05
  491.      * @access     public
  492.      * @author     Wolfram Kriesing <wk@visionp.de>
  493.      * @return     array   an empty, or pre-initialized element
  494.      */
  495.     function getDefaultValues()
  496.     {
  497.         $ret = array();
  498.         // here we read all the columns from the DB and initialize them
  499.         // with '' to prevent PHP-warnings in case we use error_reporting=E_ALL
  500.         foreach ($this->metadata() as $aCol=>$x) {
  501.             $ret[$aCol] = '';
  502.         }
  503.         return $ret;
  504.     }
  505.  
  506.     // }}}
  507.     // {{{ getEmptyElement()
  508.  
  509.     /**
  510.      * this is just for BC
  511.      * @deprecated
  512.      */
  513.     function getEmptyElement()
  514.     {
  515.         $this->getDefaultValues();
  516.     }
  517.  
  518.     // }}}
  519.     // {{{ getQueryString()
  520.  
  521.     /**
  522.      * Render the current query and return it as a string.
  523.      *
  524.      * @return string the current query
  525.      */
  526.     function getQueryString()
  527.     {
  528.         $ret = $this->_buildSelectQuery();
  529.         if (is_string($ret)) {
  530.             $ret = trim($ret);
  531.         }
  532.         return $ret;
  533.     }
  534.  
  535.     // }}}
  536.     // {{{ save()
  537.  
  538.     /**
  539.      * save data, calls either update or add
  540.      * if the primaryCol is given in the data this method knows that the
  541.      * data passed to it are meant to be updated (call 'update'), otherwise it will
  542.      * call the method 'add'.
  543.      * If you dont like this behaviour simply stick with the methods 'add'
  544.      * and 'update' and ignore this one here.
  545.      * This method is very useful when you have validation checks that have to
  546.      * be done for both adding and updating, then you can simply overwrite this
  547.      * method and do the checks in here, and both cases will be validated first.
  548.      *
  549.      * @version    2002/03/11
  550.      * @access     public
  551.      * @author     Wolfram Kriesing <wk@visionp.de>
  552.      * @param      array   contains the new data that shall be saved in the DB
  553.      * @return     mixed   the data returned by either add or update-method
  554.      */
  555.     function save($data)
  556.     {
  557.         if (!empty($data[$this->primaryCol])) {
  558.             return $this->update($data);
  559.         }
  560.         return $this->add($data);
  561.     }
  562.  
  563.     // }}}
  564.     // {{{ update()
  565.  
  566.     /**
  567.      * update the member data of a data set
  568.      *
  569.      * @version    2002/03/06
  570.      * @access     public
  571.      * @author     Wolfram Kriesing <wk@visionp.de>
  572.      * @param      array   contains the new data that shall be saved in the DB
  573.      *                     the id has to be given in the field with the key 'ID'
  574.      * @return     mixed   true on success, or false otherwise
  575.      */
  576.     function update($newData)
  577.     {
  578.         $query = array();
  579.         // do only set the 'where' part in $query, if a primary column is given
  580.         // if not the default 'where' clause is used
  581.         if (isset($newData[$this->primaryCol])) {
  582.             $query['where'] = $this->primaryCol.'='.$newData[$this->primaryCol];
  583.         }
  584.         $newData = $this->_checkColumns($newData, 'update');
  585.         $values = array();
  586.         $raw = $this->getOption('raw');
  587.         foreach ($newData as $key => $aData) {         // quote the data
  588.             //$values[] = "{$this->table}.$key=". ($raw ? $aData : $this->db->quote($aData));
  589.             $values[] = "$key=". ($raw ? $aData : $this->db->quote($aData));
  590.         }
  591.  
  592.         $query['set'] = implode(',', $values);
  593. //FIXXXME _buildUpdateQuery() seems to take joins into account, whcih is bullshit here
  594.         $updateString = $this->_buildUpdateQuery($query);
  595. #print '$updateString = '.$updateString;
  596.         return $this->execute($updateString, 'query') ? true : false;
  597.     }
  598.  
  599.     // }}}
  600.     // {{{ add()
  601.  
  602.     /**
  603.      * add a new member in the DB
  604.      *
  605.      * @version    2002/04/02
  606.      * @access     public
  607.      * @author     Wolfram Kriesing <wk@visionp.de>
  608.      * @param      array   contains the new data that shall be saved in the DB
  609.      * @return     mixed   the inserted id on success, or false otherwise
  610.      */
  611.     function add($newData)
  612.     {
  613.         // if no primary col is given, get next sequence value
  614.         if (empty($newData[$this->primaryCol])) {
  615.             if ($this->primaryCol) {    // do only use the sequence if a primary column is given
  616.                                         // otherwise the data are written as given
  617.                 $id = $this->db->nextId($this->sequenceName);
  618.                 $newData[$this->primaryCol] = (int)$id;
  619.             } else {
  620.                 // if no primary col is given return true on success
  621.                 $id = true;
  622.             }
  623.         } else {
  624.             $id = $newData[$this->primaryCol];
  625.         }
  626.  
  627.         //unset($newData[$this->primaryCol]);
  628.  
  629.         $newData = $this->_checkColumns($newData, 'add');
  630.         $newData = $this->_quoteArray($newData);
  631.  
  632.         $query = sprintf(   'INSERT INTO %s (%s) VALUES (%s)',
  633.                             $this->table,
  634.                             implode(', ', array_keys($newData)),
  635.                             implode(', ', $newData)
  636.                        );
  637.         return $this->execute($query, 'query') ? $id : false;
  638.     }
  639.  
  640.     // }}}
  641.     // {{{ addMultiple()
  642.  
  643.     /**
  644.      * adds multiple new members in the DB
  645.      *
  646.      * @version    2002/07/17
  647.      * @access     public
  648.      * @author     Wolfram Kriesing <wk@visionp.de>
  649.      * @param      array   contains an array of new data that shall be saved in the DB
  650.      *                     the key-value pairs have to be the same for all the data!!!
  651.      * @return     mixed   the inserted ids on success, or false otherwise
  652.      */
  653.     function addMultiple($data)
  654.     {
  655.         if (!sizeof($data)) {
  656.             return false;
  657.         }
  658.  
  659.         // the inserted ids which will be returned or if no primaryCol is given
  660.         // we return true by default
  661.         $retIds = $this->primaryCol ? array() : true;
  662.         $allData = array();     // each row that will be inserted
  663.         foreach ($data as $key => $aData) {
  664.             $aData = $this->_checkColumns($aData, 'add');
  665.             $aData = $this->_quoteArray($aData);
  666.  
  667.             if (empty($aData[$this->primaryCol])) {
  668.                 if ($this->primaryCol) {    // do only use the sequence if a primary column is given
  669.                                             // otherwise the data are written as given
  670.                     $retIds[] = $id = (int)$this->db->nextId($this->sequenceName);
  671.                     $aData[$this->primaryCol] = $id;
  672.                 }
  673.             } else {
  674.                 $retIds[] = $aData[$this->primaryCol];
  675.             }
  676.             $allData[] = '('.implode(', ', $aData).')';
  677.         }
  678.  
  679.         $query = sprintf(   'INSERT INTO %s (%s) VALUES %s',
  680.                             $this->table,
  681.                             implode(', ', array_keys($aData)), // use the keys of the last element built
  682.                             implode(', ', $allData)
  683.                         );
  684.         return $this->execute($query, 'query') ? $retIds : false;
  685.     }
  686.  
  687.     // }}}
  688.     // {{{ remove()
  689.  
  690.    /**
  691.      * removes a member from the DB
  692.      *
  693.      * @version    2002/04/08
  694.      * @access     public
  695.      * @author     Wolfram Kriesing <wk@visionp.de>
  696.      * @param      mixed   integer/string - the value of the column that shall be removed
  697.      *                       array   - multiple columns that shall be matched, the second parameter will be ommited
  698.      * @param      string  the column to match the data against, only if $data is not an array
  699.      * @return     boolean
  700.      */
  701.     function remove($data, $whereCol='')
  702.     {
  703.         $raw = $this->getOption('raw');
  704.  
  705.         if (is_array($data)) {
  706. //FIXXME check $data if it only contains columns that really exist in the table
  707.             $wheres = array();
  708.             foreach ($data as $key => $val) {
  709.                 $wheres[] = $key.'='. ($raw ? $val : $this->db->quote($val));
  710.             }
  711.             $whereClause = implode(' AND ',$wheres);
  712.         } else {
  713.             if (empty($whereCol)) {
  714.                 $whereCol = $this->primaryCol;
  715.             }
  716.             $whereClause = $whereCol.'='. ($raw ? $data : $this->db->quote($data));
  717.         }
  718.  
  719.         $query = sprintf(   'DELETE FROM %s WHERE %s',
  720.                             $this->table,
  721.                             $whereClause
  722.                             );
  723.         return $this->execute($query, 'query') ? true : false;
  724. // i think this method should return the ID's that it removed, this way we could simply use the result
  725. // for further actions that depend on those id ... or? make stuff easier, see ignaz::imail::remove
  726.     }
  727.  
  728.     // }}}
  729.     // {{{ removeAll()
  730.  
  731.     /**
  732.      * empty a table
  733.      *
  734.      * @version    2002/06/17
  735.      * @access     public
  736.      * @author     Wolfram Kriesing <wk@visionp.de>
  737.      * @return
  738.      */
  739.     function removeAll()
  740.     {
  741.         $query = 'DELETE FROM '.$this->table;
  742.         return $this->execute($query, 'query') ? true : false;
  743.     }
  744.  
  745.     // }}}
  746.     // {{{ removeMultiple()
  747.  
  748.     /**
  749.      * remove the datasets with the given ids
  750.      *
  751.      * @version    2002/04/24
  752.      * @access     public
  753.      * @author     Wolfram Kriesing <wk@visionp.de>
  754.      * @param      array   the ids to remove
  755.      * @return
  756.      */
  757.     function removeMultiple($ids, $colName='')
  758.     {
  759.         if (empty($colName)) {
  760.             $colName = $this->primaryCol;
  761.         }
  762.         $ids = $this->_quoteArray($ids);
  763.  
  764.         $query = sprintf(   'DELETE FROM %s WHERE %s IN (%s)',
  765.                             $this->table,
  766.                             $colName,
  767.                             implode(',', $ids)
  768.                         );
  769.         return $this->execute($query, 'query') ? true : false;
  770.     }
  771.  
  772.     // }}}
  773.     // {{{ removePrimary()
  774.  
  775.     /**
  776.      * removes a member from the DB and calls the remove methods of the given objects
  777.      * so all rows in another table that refer to this table are erased too
  778.      *
  779.      * @version    2002/04/08
  780.      * @access     public
  781.      * @author     Wolfram Kriesing <wk@visionp.de>
  782.      * @param      integer the value of the primary key
  783.      * @param      string  the column name of the tables with the foreign keys
  784.      * @param      object  just for convinience, so nobody forgets to call this method
  785.      *                       with at least one object as a parameter
  786.      * @return     boolean
  787.      */
  788.     function removePrimary($id, $colName, $atLeastOneObject)
  789.     {
  790.         $argCounter = 2;    // we have 2 parameters that need to be given at least
  791.         // func_get_arg returns false and a warning if there are no more parameters, so
  792.         // we suppress the warning and check for false
  793.         while ($object = @func_get_arg($argCounter++)) {
  794. //FIXXXME let $object also simply be a table name
  795.             if (!$object->remove($id, $colName)) {
  796. //FIXXXME do this better
  797.                 $this->_errorSet("Error removing '$colName=$id' from table {$object->table}.");
  798.                return false;
  799.             }
  800.         }
  801.  
  802.         return ($this->remove($id) ? true : false);
  803.     }
  804.  
  805.     // }}}
  806.     // {{{ setLimit()
  807.  
  808.     /**
  809.      * @param integer $from
  810.      * @param integer $count
  811.      */
  812.     function setLimit($from=0, $count=0)
  813.     {
  814.         if ($from==0 && $count==0) {
  815.             $this->_limit = array();
  816.         } else {
  817.             $this->_limit = array($from, $count);
  818.         }
  819.     }
  820.  
  821.     // }}}
  822.     // {{{ getLimit()
  823.  
  824.     /**
  825.      * @return array
  826.      */
  827.     function getLimit()
  828.     {
  829.         return $this->_limit;
  830.     }
  831.  
  832.     // }}}
  833.     // {{{ setWhere()
  834.  
  835.     /**
  836.      * sets the where condition which is used for the current instance
  837.      *
  838.      * @version    2002/04/16
  839.      * @access     public
  840.      * @author     Wolfram Kriesing <wk@visionp.de>
  841.      * @param      string  the where condition, this can be complete like 'X=7 AND Y=8'
  842.      */
  843.     function setWhere($whereCondition='')
  844.     {
  845.         $this->_where = $whereCondition;
  846. //FIXXME parse the where condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'"
  847. // then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
  848.     }
  849.  
  850.     // }}}
  851.     // {{{ getWhere()
  852.  
  853.     /**
  854.      * gets the where condition which is used for the current instance
  855.      *
  856.      * @version    2002/04/22
  857.      * @access     public
  858.      * @author     Wolfram Kriesing <wk@visionp.de>
  859.      * @return     string  the where condition, this can be complete like 'X=7 AND Y=8'
  860.      */
  861.     function getWhere()
  862.     {
  863.         return $this->_where;
  864.     }
  865.  
  866.     // }}}
  867.     // {{{ addWhere()
  868.  
  869.     /**
  870.      * only adds a string to the where clause
  871.      *
  872.      * @version    2002/07/22
  873.      * @access     public
  874.      * @author     Wolfram Kriesing <wk@visionp.de>
  875.      * @param      string  the where clause to add to the existing one
  876.      * @param      string  the condition for how to concatenate the new where clause
  877.      *                       to the existing one
  878.      */
  879.     function addWhere($where, $condition='AND')
  880.     {
  881.         if ($this->getWhere()) {
  882.             $where = $this->getWhere().' '.$condition.' '.$where;
  883.         }
  884.         $this->setWhere($where);
  885.     }
  886.  
  887.     // }}}
  888.     // {{{ addWhere()
  889.  
  890.     /**
  891.      * add a where-like clause which works like a search for the given string
  892.      * i.e. calling it like this:
  893.      *     $this->addWhereSearch('name', 'otto hans')
  894.      * produces a where clause like this one
  895.      *     LOWER(name) LIKE "%otto%hans%"
  896.      * so the search finds the given string
  897.      *
  898.      * @version    2002/08/14
  899.      * @access     public
  900.      * @author     Wolfram Kriesing <wk@visionp.de>
  901.      * @param      string  the column to search in for
  902.      * @param      string  the string to search for
  903.      */
  904.     function addWhereSearch($column, $string, $condition='AND')
  905.     {
  906.         // if the column doesnt contain a tablename use the current table name in case it is a defined column
  907.         // to prevent ambigious rows
  908.         if (strpos($column, '.') === false) {
  909.             $meta = $this->metadata();
  910.             if (isset($meta[$column])) {
  911.                 $column = $this->table.".$column";
  912.             }
  913.         }
  914.  
  915.         $string = $this->db->quote('%'.str_replace(' ', '%', strtolower($string)).'%');
  916.         $this->addWhere("LOWER($column) LIKE $string", $condition);
  917.     }
  918.  
  919.     // }}}
  920.     // {{{ setOrder()
  921.  
  922.     /**
  923.      * sets the order condition which is used for the current instance
  924.      *
  925.      * @version    2002/05/16
  926.      * @access     public
  927.      * @author     Wolfram Kriesing <wk@visionp.de>
  928.      * @param      string  the where condition, this can be complete like 'X=7 AND Y=8'
  929.      * @param      boolean sorting order (TRUE => ASC, FALSE => DESC)
  930.      */
  931.     function setOrder($orderCondition='', $desc=false)
  932.     {
  933.         $this->_order = $orderCondition .($desc ? ' DESC' : '');
  934.     }
  935.  
  936.     // }}}
  937.     // {{{ addOrder()
  938.  
  939.     /**
  940.      * Add a order parameter to the query.
  941.      *
  942.      * @version    2003/05/28
  943.      * @access     public
  944.      * @author     Wolfram Kriesing <wk@visionp.de>
  945.      * @param      string  the where condition, this can be complete like 'X=7 AND Y=8'
  946.      * @param      boolean sorting order (TRUE => ASC, FALSE => DESC)
  947.      */
  948.     function addOrder($orderCondition='', $desc=false)
  949.     {
  950.         $order = $orderCondition .($desc ? ' DESC' : '');
  951.         if ($this->_order) {
  952.             $this->_order = $this->_order.','.$order;
  953.         } else {
  954.             $this->_order = $order;
  955.         }
  956.     }
  957.  
  958.     // }}}
  959.     // {{{ getOrder()
  960.  
  961.     /**
  962.      * gets the order condition which is used for the current instance
  963.      *
  964.      * @version    2002/05/16
  965.      * @access     public
  966.      * @author     Wolfram Kriesing <wk@visionp.de>
  967.      * @return     string  the order condition, this can be complete like 'ID,TIMESTAMP DESC'
  968.      */
  969.     function getOrder()
  970.     {
  971.         return $this->_order;
  972.     }
  973.  
  974.     // }}}
  975.     // {{{ setHaving()
  976.  
  977.     /**
  978.      * sets the having definition
  979.      *
  980.      * @version    2003/06/05
  981.      * @access     public
  982.      * @author     Johannes Schaefer <johnschaefer@gmx.de>
  983.      * @param      string  the having definition
  984.      */
  985.     function setHaving($having='')
  986.     {
  987.         $this->_having = $having;
  988.     }
  989.  
  990.     // }}}
  991.     // {{{ getHaving()
  992.  
  993.     /**
  994.      * gets the having definition which is used for the current instance
  995.      *
  996.      * @version    2003/06/05
  997.      * @access     public
  998.      * @author     Johannes Schaefer <johnschaefer@gmx.de>
  999.      * @return     string  the having definition
  1000.      */
  1001.     function getHaving()
  1002.     {
  1003.         return $this->_having;
  1004.     }
  1005.  
  1006.     // }}}
  1007.     // {{{ addHaving()
  1008.  
  1009.     /**
  1010.      * Extend the current having clause. This is very useful, when you are building
  1011.      * this clause from different places and dont want to overwrite the currently
  1012.      * set having clause, but extend it.
  1013.      *
  1014.      * @param string this is a having clause, i.e. 'column' or 'table.column' or 'MAX(column)'
  1015.      * @param string the connection string, which usually stays the default, which is ',' (a comma)
  1016.      */
  1017.     function addHaving($what='*', $connectString=' AND ')
  1018.     {
  1019.         if ($this->_having) {
  1020.             $this->_having = $this->_having.$connectString.$what;
  1021.         } else {
  1022.             $this->_having = $what;
  1023.         }
  1024.     }
  1025.  
  1026.     // }}}
  1027.     // {{{ setJoin()
  1028.  
  1029.     /**
  1030.      * sets a join-condition
  1031.      *
  1032.      * @version    2002/06/10
  1033.      * @access     public
  1034.      * @author     Wolfram Kriesing <wk@visionp.de>
  1035.      * @param      mixed   either a string or an array that contains
  1036.      *                       the table(s) to join on the current table
  1037.      * @param      string  the where clause for the join
  1038.      */
  1039.     function setJoin($table=null, $where=null, $joinType='default')
  1040.     {
  1041. //FIXXME make it possible to pass a table name as a string like this too 'user u'
  1042. // where u is the string that can be used to refer to this table in a where/order
  1043. // or whatever condition
  1044. // this way it will be possible to join tables with itself, like setJoin(array('user u','user u1'))
  1045. // this wouldnt work yet, but for doing so we would need to change the _build methods too!!!
  1046. // because they use getJoin('tables') and this simply returns all the tables in use
  1047. // but don't take care of the mentioned syntax
  1048.  
  1049.         if (is_null($table) || is_null($where)) {   // remove the join if not sufficient parameters are given
  1050.             $this->_join[$joinType] = array();
  1051.             return;
  1052.         }
  1053.  
  1054.         settype($table, 'array');
  1055.         $this->_join[$joinType]['table'] = $table;
  1056. /* this causes problems if we use the order-by, since it doenst know the name to order it by ... :-)
  1057.         // replace the table names with the internal name used for the join
  1058.         // this way we can also join one table multiple times if it will be implemented one day
  1059.         $this->_join['where'] = preg_replace('/'.$table.'/','j1',$where);
  1060. */
  1061.         $this->_join[$joinType]['where'] = $where;
  1062.     }
  1063.  
  1064.     // }}}
  1065.     // {{{ setJoin()
  1066.  
  1067.     /**
  1068.      * if you do a left join on $this->table you will get all entries
  1069.      * from $this->table, also if there are no entries for them in the joined table
  1070.      * if both parameters are not given the left-join will be removed
  1071.      * NOTE: be sure to only use either a right or a left join
  1072.      *
  1073.      * @version    2002/07/22
  1074.      * @access     public
  1075.      * @author     Wolfram Kriesing <wk@visionp.de>
  1076.      * @param      string  the table(s) to be left-joined
  1077.      * @param      string  the where clause for the join
  1078.      */
  1079.     function setLeftJoin($table=null, $where=null)
  1080.     {
  1081.         $this->setJoin($table, $where, 'left');
  1082.     }
  1083.  
  1084.     // }}}
  1085.     // {{{ addLeftJoin()
  1086.  
  1087.     /**
  1088.      * @param string the table to be left-joined
  1089.      * @param string the where clause for the join
  1090.      * @param string the join type
  1091.      */
  1092.     function addLeftJoin($table, $where, $type='left')
  1093.     {
  1094.         // init value, to prevent E_ALL-warning
  1095.         if (!isset($this->_join[$type]) || !$this->_join[$type]) {
  1096.             $this->_join[$type] = array('table' => array(), 'where' => '');
  1097.         }
  1098.         $this->_join[$type]['table'] = array_merge($this->_join[$type]['table'], $table);
  1099.         if (!is_array($this->_join[$type]['where'])) {
  1100.             settype($this->_join[$type]['where'], 'array');
  1101.         }
  1102.         $this->_join[$type]['where'][] = $where;
  1103.     }
  1104.  
  1105.     // }}}
  1106.     // {{{ setRightJoin()
  1107.  
  1108.     /**
  1109.      * see setLeftJoin for further explaination on what a left/right join is
  1110.      * NOTE: be sure to only use either a right or a left join
  1111. //FIXXME check if the above sentence is necessary and if sql doesnt allow the use of both
  1112.      *
  1113.      * @see        setLeftJoin()
  1114.      * @version    2002/09/04
  1115.      * @access     public
  1116.      * @author     Wolfram Kriesing <wk@visionp.de>
  1117.      * @param      string  the table(s) to be right-joined
  1118.      * @param      string  the where clause for the join
  1119.      */
  1120.     function setRightJoin($table=null, $where=null)
  1121.     {
  1122.         $this->setJoin($table, $where, 'right');
  1123.     }
  1124.  
  1125.     // }}}
  1126.     // {{{ getJoin()
  1127.  
  1128.     /**
  1129.      * gets the join-condition
  1130.      *
  1131.      * @version    2002/06/10
  1132.      * @access     public
  1133.      * @author     Wolfram Kriesing <wk@visionp.de>
  1134.      * @return     array   gets the join parameters
  1135.      */
  1136.     function getJoin($what=null)
  1137.     {
  1138.         // if the user requests all the join data or if the join is empty, return it
  1139.         if (is_null($what) || empty($this->_join)) {
  1140.             $ret = $this->_join;
  1141.         }
  1142.  
  1143.         switch (strtolower($what)) {
  1144.             case 'table':
  1145.             case 'tables':
  1146.                 $ret = array();
  1147.                 foreach ($this->_join as $aJoin) {
  1148.                     if (isset($aJoin['table']) && sizeof($aJoin['table'])) {
  1149.                         $ret = array_merge($ret, $aJoin['table']);
  1150.                     }
  1151.                 }
  1152.                 break;
  1153.             case 'right':   // return right-join data only
  1154.             case 'left':    // return left join data only
  1155.                 break;
  1156.         }
  1157.  
  1158.         return $ret;
  1159.     }
  1160.  
  1161.     // }}}
  1162.     // {{{ addJoin()
  1163.  
  1164.     /**
  1165.      *   adds a table and a where clause that shall be used for the join
  1166.      *   instead of calling
  1167.      *       setJoin(array(table1,table2),'<where clause1> AND <where clause2>')
  1168.      *   you can also call
  1169.      *       setJoin(table1,'<where clause1>')
  1170.      *       addJoin(table2,'<where clause2>')
  1171.      *   or where it makes more sense is to build a query which is made out of a
  1172.      *   left join and a standard join
  1173.      *       setLeftJoin(table1,'<where clause1>')
  1174.      *       // results in ... FROM $this->table LEFT JOIN table ON <where clause1>
  1175.      *       addJoin(table2,'<where clause2>')
  1176.      *       // results in ...  FROM $this->table,table2 LEFT JOIN table ON <where clause1> WHERE <where clause2>
  1177.      *
  1178.      * @version    2002/07/22
  1179.      * @access     public
  1180.      * @author     Wolfram Kriesing <wk@visionp.de>
  1181.      * @param      string the table to be joined
  1182.      * @param      string the where clause for the join
  1183.      * @param      string the join type
  1184.      */
  1185.     function addJoin($table, $where, $type='default')
  1186.     {
  1187.         settype($table, 'array');
  1188.  
  1189.         // init value, to prevent E_ALL-warning
  1190.         if (!isset($this->_join[$type]) || !$this->_join[$type]) {
  1191.             $this->_join[$type] = array('table' => array(), 'where' => '');
  1192.         }
  1193.         $this->_join[$type]['table'] = array_merge($this->_join[$type]['table'], $table);
  1194.         $this->_join[$type]['where'] .= trim($this->_join[$type]['where']) ? ' AND '.$where : $where;
  1195.     }
  1196.  
  1197.     // }}}
  1198.     // {{{ setTable()
  1199.  
  1200.     /**
  1201.      * sets the table this class is currently working on
  1202.      *
  1203.      * @version    2002/07/11
  1204.      * @access     public
  1205.      * @author     Wolfram Kriesing <wk@visionp.de>
  1206.      * @param      string  the table name
  1207.      */
  1208.     function setTable($table)
  1209.     {
  1210.         $this->table = $table;
  1211.     }
  1212.  
  1213.     // }}}
  1214.     // {{{ getTable()
  1215.  
  1216.     /**
  1217.      * gets the table this class is currently working on
  1218.      *
  1219.      * @version    2002/07/11
  1220.      * @access     public
  1221.      * @author     Wolfram Kriesing <wk@visionp.de>
  1222.      * @return     string  the table name
  1223.      */
  1224.     function getTable()
  1225.     {
  1226.         return $this->table;
  1227.     }
  1228.  
  1229.     // }}}
  1230.     // {{{ setGroup()
  1231.  
  1232.     /**
  1233.      * sets the group-by condition
  1234.      *
  1235.      * @version    2002/07/22
  1236.      * @access     public
  1237.      * @author     Wolfram Kriesing <wk@visionp.de>
  1238.      * @param      string  the group condition
  1239.      */
  1240.     function setGroup($group='')
  1241.     {
  1242.         $this->_group = $group;
  1243. //FIXXME parse the condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'"
  1244. // then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
  1245.     }
  1246.  
  1247.     // }}}
  1248.     // {{{ getGroup()
  1249.  
  1250.     /**
  1251.      *   gets the group condition which is used for the current instance
  1252.      *
  1253.      * @version    2002/07/22
  1254.      * @access     public
  1255.      * @author     Wolfram Kriesing <wk@visionp.de>
  1256.      * @return     string  the group condition
  1257.      */
  1258.     function getGroup()
  1259.     {
  1260.         return $this->_group;
  1261.     }
  1262.  
  1263.     // }}}
  1264.     // {{{ setSelect()
  1265.  
  1266.     /**
  1267.      * limit the result to return only the columns given in $what
  1268.      * @param string fields that shall be selected
  1269.      */
  1270.     function setSelect($what='*')
  1271.     {
  1272.         $this->_select = $what;
  1273.     }
  1274.  
  1275.     // }}}
  1276.     // {{{ addSelect()
  1277.  
  1278.     /**
  1279.      * add a string to the select part of the query
  1280.      *
  1281.      * add a string to the select-part of the query and connects it to an existing
  1282.      * string using the $connectString, which by default is a comma.
  1283.      * (SELECT xxx FROM - xxx is the select-part of a query)
  1284.      *
  1285.      * @version    2003/01/08
  1286.      * @access     public
  1287.      * @author     Wolfram Kriesing <wk@visionp.de>
  1288.      * @param      string  the string that shall be added to the select-part
  1289.      * @param      string  the string to connect the new string with the existing one
  1290.      * @return     void
  1291.      */
  1292.     function addSelect($what='*', $connectString=',')
  1293.     {
  1294.         // if the select string is not empty add the string, otherwise simply set it
  1295.         if ($this->_select) {
  1296.             $this->_select = $this->_select.$connectString.$what;
  1297.         } else {
  1298.             $this->_select = $what;
  1299.         }
  1300.     }
  1301.  
  1302.     // }}}
  1303.     // {{{ getSelect()
  1304.  
  1305.     /**
  1306.      * @return     string
  1307.      */
  1308.     function getSelect()
  1309.     {
  1310.         return $this->_select;
  1311.     }
  1312.  
  1313.     // }}}
  1314.     // {{{ setDontSelect()
  1315.  
  1316.     /**
  1317.      * @param     string
  1318.      */
  1319.     function setDontSelect($what='')
  1320.     {
  1321.         $this->_dontSelect = $what;
  1322.     }
  1323.  
  1324.     // }}}
  1325.     // {{{ getDontSelect()
  1326.  
  1327.     /**
  1328.      * @return     string
  1329.      */
  1330.     function getDontSelect()
  1331.     {
  1332.         return $this->_dontSelect;
  1333.     }
  1334.  
  1335.     // }}}
  1336.     // {{{ reset()
  1337.  
  1338.     /**
  1339.      * reset all the set* settings; with no parameter given, it resets them all
  1340.      *
  1341.      * @version    2002/09/16
  1342.      * @access     public
  1343.      * @author     Wolfram Kriesing <wk@visionp.de>
  1344.      * @return     void
  1345.      */
  1346.     function reset($what=array())
  1347.     {
  1348.         if (!sizeof($what)) {
  1349.             $what = array(
  1350.                 'select',
  1351.                 'dontSelect',
  1352.                 'group',
  1353.                 'having',
  1354.                 'limit',
  1355.                 'where',
  1356.                 'index',
  1357.                 'order',
  1358.                 'join',
  1359.                 'leftJoin',
  1360.                 'rightJoin'
  1361.             );
  1362.         }
  1363.  
  1364.         foreach ($what as $aReset) {
  1365.             $this->{'set'.ucfirst($aReset)}();
  1366.         }
  1367.     }
  1368.  
  1369.     // }}}
  1370.     // {{{ setOption()
  1371.  
  1372.     /**
  1373.      * set mode the class shall work in
  1374.      * currently we have the modes:
  1375.      * 'raw'   does not quote the data before building the query
  1376.      *
  1377.      * @version    2002/09/17
  1378.      * @access     public
  1379.      * @author     Wolfram Kriesing <wk@visionp.de>
  1380.      * @param      string      the mode to be set
  1381.      * @param      mixed       the value of the mode
  1382.      * @return     void
  1383.      */
  1384.     function setOption($option, $value)
  1385.     {
  1386.         $this->options[strtolower($option)] = $value;
  1387.     }
  1388.  
  1389.     // }}}
  1390.     // {{{ getOption()
  1391.  
  1392.     /**
  1393.      * @return     string
  1394.      */
  1395.     function getOption($option)
  1396.     {
  1397.         return $this->options[strtolower($option)];
  1398.     }
  1399.  
  1400.     // }}}
  1401.     // {{{ _quoteArray()
  1402.  
  1403.     /**
  1404.      * quotes all the data in this array if we are not in raw mode!
  1405.      * @param array
  1406.      */
  1407.     function _quoteArray($data)
  1408.     {
  1409.         if (!$this->getOption('raw')) {
  1410.             foreach ($data as $key => $val) {
  1411.                 $data[$key] = $this->db->quote($val);
  1412.             }
  1413.         }
  1414.         return $data;
  1415.     }
  1416.  
  1417.     // }}}
  1418.     // {{{ _checkColumns()
  1419.  
  1420.     /**
  1421.      * checks if the columns which are given as the array's indexes really exist
  1422.      * if not it will be unset anyway
  1423.      *
  1424.      * @version    2002/04/16
  1425.      * @access     public
  1426.      * @author     Wolfram Kriesing <wk@visionp.de>
  1427.      * @param      string  the actual message, first word should always be the method name,
  1428.      *                       to build the message like this: className::methodname
  1429.      * @param      integer the line number
  1430.      */
  1431.     function _checkColumns($newData, $method='unknown')
  1432.     {
  1433.         if (!$meta = $this->metadata()) {   // if no metadata available, return data as given
  1434.             return $newData;
  1435.         }
  1436.  
  1437.         foreach ($newData as $colName => $x) {
  1438.             if (!isset($meta[$colName])) {
  1439.                 $this->_errorLog("$method, column {$this->table}.$colName doesnt exist, value was removed before '$method'",__LINE__);
  1440.                 unset($newData[$colName]);
  1441.             } else {
  1442.                 // if the current column exists, check the length too, not to write content that is too long
  1443.                 // prevent DB-errors here
  1444.                 // do only check the data length if this field is given
  1445. // FIXXME use PEAR-defined field for 'DATA_LENGTH'
  1446.                 if (isset($meta[$colName]['DATA_LENGTH']) &&
  1447.                     ($oldLength=strlen($newData[$colName])) > $meta[$colName]['DATA_LENGTH']) {
  1448.  
  1449.                     $this->_errorLog("_checkColumns, had to trim column '$colName' from $oldLength to ".
  1450.                                         $meta[$colName]['DATA_LENGTH'].' characters.', __LINE__);
  1451.                     $newData[$colName] = substr($newData[$colName], 0, $meta[$colName]['DATA_LENGTH']);
  1452.                 }
  1453.             }
  1454.         }
  1455.         return $newData;
  1456.     }
  1457.  
  1458.     // }}}
  1459.     // {{{ debug()
  1460.  
  1461.     /**
  1462.      * overwrite this method and i.e. print the query $string
  1463.      * to see the final query
  1464.      *
  1465.      * @param      string  the query mostly
  1466.      */
  1467.     function debug($string){}
  1468.  
  1469.     //
  1470.     //
  1471.     //  ONLY ORACLE SPECIFIC, not very nice since it is DB dependent, but we need it!!!
  1472.     //
  1473.     //
  1474.  
  1475.     // }}}
  1476.     // {{{ metadata()
  1477.  
  1478.     /**
  1479.      * !!!! query COPIED FROM db_oci8.inc - from PHPLIB !!!!
  1480.      *
  1481.      * @access   public
  1482.      * @see
  1483.      * @version  2001/09
  1484.      * @author   PHPLIB
  1485.      * @param
  1486.      * @return
  1487.      */
  1488.     function metadata($table='')
  1489.     {
  1490.         // is there an alias in the table name, then we have something like this: 'user ua'
  1491.         // cut of the alias and return the table name
  1492.         if (strpos($table, ' ') !== false) {
  1493.             $split = explode(' ', trim($table));
  1494.             $table = $split[0];
  1495.         }
  1496.  
  1497.         $full = false;
  1498.         if (empty($table)) {
  1499.             $table = $this->table;
  1500.         }
  1501.         // to prevent multiple selects for the same metadata
  1502.         if (isset($this->_metadata[$table])) {
  1503.             return $this->_metadata[$table];
  1504.         }
  1505.  
  1506. // FIXXXME use oci8 implementation of newer PEAR::DB-version
  1507.         if ($this->db->phptype == 'oci8') {
  1508.             $count = 0;
  1509.             $id    = 0;
  1510.             $res   = array();
  1511.  
  1512.             //# This is a RIGHT OUTER JOIN: "(+)", if you want to see, what
  1513.             //# this query results try the following:
  1514.             //// $table = new Table; $this->db = new my_DB_Sql; // you have to make
  1515.             ////                                          // your own class
  1516.             //// $table->show_results($this->db->query(see query vvvvvv))
  1517.             ////
  1518.             $res = $this->db->getAll("SELECT T.column_name,T.table_name,T.data_type,".
  1519.                 "T.data_length,T.data_precision,T.data_scale,T.nullable,".
  1520.                 "T.char_col_decl_length,I.index_name".
  1521.                 " FROM ALL_TAB_COLUMNS T,ALL_IND_COLUMNS I".
  1522.                 " WHERE T.column_name=I.column_name (+)".
  1523.                 " AND T.table_name=I.table_name (+)".
  1524.                 " AND T.table_name=UPPER('$table') ORDER BY T.column_id");
  1525.  
  1526.             if (DB::isError($res)) {
  1527.                 //$this->_errorSet($res->getMessage());
  1528.                 // i think we only need to log here, since this method is never used
  1529.                 // directly for the user's functionality, which means if it fails it
  1530.                 // is most probably an appl error
  1531.                 $this->_errorLog($res->getUserInfo());
  1532.                 return false;
  1533.             }
  1534.             foreach ($res as $key=>$val) {
  1535.                 $res[$key]['name'] = $val['COLUMN_NAME'];
  1536.             }
  1537.         } else {
  1538.             $res = $this->db->tableinfo($table);
  1539.             if (DB::isError($res)) {
  1540.                 $this->_errorSet($res->getUserInfo());
  1541.                 return false;
  1542.             }
  1543.         }
  1544.  
  1545.         $ret = array();
  1546.         foreach ($res as $key => $val) {
  1547.             $ret[$val['name']] = $val;
  1548.         }
  1549.         $this->_metadata[$table] = $ret;
  1550.         return $ret;
  1551.     }
  1552.  
  1553.  
  1554.  
  1555.     //
  1556.     //  methods for building the query
  1557.     //
  1558.  
  1559.     // }}}
  1560.     // {{{ _buildFrom()
  1561.  
  1562.     /**
  1563.      * build the from string
  1564.      *
  1565.      * @version    2002/07/11
  1566.      * @access     public
  1567.      * @author     Wolfram Kriesing <wk@visionp.de>
  1568.      * @return     string  the string added behind FROM
  1569.      */
  1570.     function _buildFrom()
  1571.     {
  1572.         $from = $this->table;
  1573.  
  1574.         if ($join = $this->getJoin()) {  // is join set?
  1575.             // handle the standard join thingy
  1576.             if (@$join['default']) {
  1577.                 $from .= ','.implode(',',$join['default']['table']);
  1578.             }
  1579.  
  1580.             // if we also have a left join, add the 'LEFT JOIN table ON condition'
  1581.             // use isset to prevent E_ALL warnings
  1582.             $joinType = isset($join['left']) && $join['left'] ? 'left' :
  1583.                         (isset($join['right']) && $join['right'] ? 'right' : false);
  1584. // this class can only handle one kind of join at a time ... how stupid :-)
  1585.  
  1586.             if ($joinType) { // do we have any of the above checked join-types?
  1587.                 $joinExpr = ' '.strtoupper($joinType).' JOIN ';
  1588.                 $tables = $join[$joinType]['table'];
  1589.                 settype($tables,'array');
  1590.                 $wheres = $join[$joinType]['where'];
  1591.                 settype($wheres,'array');
  1592.                 foreach ($wheres as $k => $where) {
  1593.                     $from .= $joinExpr.$tables[$k];
  1594.                     // replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
  1595.                     // since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
  1596.     // FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
  1597.                     $regExp = '/_('.implode('|', $join[$joinType]['table']).')_([^\s]+)/';
  1598.                     $where = preg_replace($regExp, '$1.$2', $where);
  1599.  
  1600.                     // add the table name before any column that has no table prefix
  1601.                     // since this might cause "unambigious column" errors
  1602.                     if ($meta = $this->metadata()) {
  1603.                         foreach ($meta as $aCol=>$x) {
  1604.                             // this covers the LIKE,IN stuff: 'name LIKE "%you%"'  'id IN (2,3,4,5)'
  1605.                             $where = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $where);
  1606.                             // replace also the column names which are behind a '='
  1607.                             // and do this also if the aCol is at the end of the where clause
  1608.                             // that's what the $ is for
  1609.                             $where = preg_replace('/=\s*'.$aCol.'(\s|$)/', "={$this->table}.$aCol ", $where);
  1610.                             // replace if colName is first and possibly also if at the beginning of the where-string
  1611.                             $where = preg_replace('/(^\s*|\s+)'.$aCol.'\s*=/', "$1{$this->table}.$aCol=", $where);
  1612.                         }
  1613.                     }
  1614.  
  1615.                     $from .= ' ON '.$where;
  1616.                 }
  1617.             }
  1618.         }
  1619.  
  1620.         return $from;
  1621.     }
  1622.  
  1623.     // }}}
  1624.     // {{{ getTableShortName()
  1625.  
  1626.     /**
  1627.      *   this method gets the short name for a table
  1628.      *
  1629.      *   get the short name for a table, this is needed to properly build the
  1630.      *   'AS' parts in the select query
  1631.      * @param  string  the real table name
  1632.      * @return string  the table's short name
  1633.      */
  1634.     function getTableShortName($table)
  1635.     {
  1636.         $tableSpec = $this->getTableSpec(false);
  1637.         if (isset($tableSpec[$table]['shortName']) && $tableSpec[$table]['shortName']) {
  1638. //print "$table ... ".$tableSpec[$table]['shortName'].'<br>';
  1639.             return $tableSpec[$table]['shortName'];
  1640.         }
  1641.  
  1642.         $possibleTableShortName = preg_replace($this->_tableNameToShortNamePreg, '', $table);
  1643. //print "$table ... $possibleTableShortName<br>";
  1644.         return $possibleTableShortName;
  1645.     }
  1646.  
  1647.     // }}}
  1648.     // {{{ getTableSpec()
  1649.  
  1650.     /**
  1651.      * gets the tableSpec either indexed by the short name or the name
  1652.      * returns the array for the tables given as parameter or if no
  1653.      * parameter given for all tables that exist in the tableSpec
  1654.      *
  1655.      * @param      array   table names (not the short names!)
  1656.      * @param      boolean if true the table is returned indexed by the shortName
  1657.      *                       otherwise indexed by the name
  1658.      * @return     array   the tableSpec indexed
  1659.      */
  1660.     function getTableSpec($shortNameIndexed=true, $tables=array())
  1661.     {
  1662.         $newSpec = array();
  1663.         foreach ($this->tableSpec as $aSpec) {
  1664.             if (sizeof($tables)==0 || in_array($aSpec['name'],$tables)) {
  1665.                 if ($shortNameIndexed) {
  1666.                     $newSpec[$aSpec['shortName']] = $aSpec;
  1667.                 } else {
  1668.                     $newSpec[$aSpec['name']] = $aSpec;
  1669.                 }
  1670.             }
  1671.         }
  1672.         return $newSpec;
  1673.     }
  1674.  
  1675.     // }}}
  1676.     // {{{ _buildSelect()
  1677.  
  1678.     /**
  1679.      *   build the 'SELECT <what> FROM ... 'for a select
  1680.      *
  1681.      * @version    2002/07/11
  1682.      * @access     public
  1683.      * @author     Wolfram Kriesing <wk@visionp.de>
  1684.      * @param      string      if given use this string
  1685.      * @return     string      the what-clause
  1686.      */
  1687.     function _buildSelect($what=null)
  1688.     {
  1689.         // what has preference, that means if what is set it is used
  1690.         // this is only because the methods like 'get' pass an individually built value, which
  1691.         // is supposed to be used, but usually it's generically build using the 'getSelect' values
  1692.         if (empty($what) && $this->getSelect()) {
  1693.             $what = $this->getSelect();
  1694.         }
  1695.  
  1696.         //
  1697.         // replace all the '*' by the real column names, and take care of the dontSelect-columns!
  1698.         //
  1699.         $dontSelect = $this->getDontSelect();
  1700.         $dontSelect = $dontSelect ? explode(',', $dontSelect) : array(); // make sure dontSelect is an array
  1701.  
  1702.         // here we will replace all the '*' and 'table.*' by all the columns that this table
  1703.         // contains. we do this so we can easily apply the 'dontSelect' values.
  1704.         // and so we can also handle queries like: 'SELECT *,count() FROM ' and 'SELECT table.*,x FROM ' too
  1705.         if (strpos($what, '*') !== false) {
  1706.             // subpattern 1 get all the table names, that are written like this: 'table.*' including '*'
  1707.             // for '*' the tablename will be ''
  1708.             preg_match_all('/([^,]*)(\.)?\*\s*(,|$)/U', $what, $res);
  1709. //print "$what ... ";print_r($res);print "<br>";
  1710.             $selectAllFromTables = array_unique($res[1]); // make the table names unique, so we do it all just once for each table
  1711.             $tables = array();
  1712.             if (in_array('', $selectAllFromTables)) { // was there a '*' ?
  1713.                 // get all the tables that we need to process, depending on if joined or not
  1714.                 $tables = $this->getJoin() ?
  1715.                                 array_merge($this->getJoin('tables'), array($this->table)) : // get the joined tables and this->table
  1716.                                 array($this->table);        // create an array with only this->table
  1717.             } else {
  1718.                 $tables = $selectAllFromTables;
  1719.             }
  1720.  
  1721.             $cols = array();
  1722.             foreach ($tables as $aTable) {      // go thru all the tables and get all columns for each, and handle 'dontSelect'
  1723.                 if ($meta = $this->metadata($aTable)) {
  1724.                     foreach ($meta as $colName => $x) {
  1725.                         // handle the dontSelect's
  1726.                         if (in_array($colName, $dontSelect) || in_array("$aTable.$colName", $dontSelect)) {
  1727.                             continue;
  1728.                         }
  1729.  
  1730.                         // build the AS clauses
  1731.                         // put " around them to enable use of reserved words, i.e. SELECT table.option as option FROM...
  1732.                         // and 'option' actually is a reserved word, at least in mysql
  1733.                         // put double quotes around them, since pgsql doesnt work with single quotes
  1734.                         if ($aTable == $this->table) {
  1735.                             $cols[$aTable][] = $this->table.".$colName AS \"$colName\"";
  1736.                         } else {
  1737.                             $cols[$aTable][] = "$aTable.$colName AS \"_".$this->getTableShortName($aTable)."_$colName\"";
  1738.                         }
  1739.                     }
  1740.                 }
  1741.             }
  1742.  
  1743.             // put the extracted select back in the $what
  1744.             // that means replace 'table.*' by the i.e. 'table.id AS _table_id'
  1745.             // or if it is the table of this class replace 'table.id AS id'
  1746.             if (in_array('', $selectAllFromTables)) {
  1747.                 $allCols = array();
  1748.                 foreach ($cols as $aTable) {
  1749.                     $allCols[] = implode(',', $aTable);
  1750.                 }
  1751.                 $what = preg_replace('/(^|,)\*($|,)/', '$1'.implode(',',$allCols).'$2', $what);
  1752.                 // remove all the 'table.*' since we have selected all anyway (because there was a '*' in the select)
  1753.                 $what = preg_replace('/[^,]*(\.)?\*\s*(,|$)/U', '', $what);
  1754.             } else {
  1755.                 foreach ($cols as $tableName => $aTable) {
  1756.                     if (is_array($aTable) && sizeof($aTable)) {
  1757.                         // replace all the 'table.*' by their select of each column
  1758.                         $what = preg_replace('/(^|,)\s*'.$tableName.'\.\*\s*($|,)/', '$1'.implode(',',$aTable).'$2', $what);
  1759.                     }
  1760.                 }
  1761.             }
  1762.         }
  1763.  
  1764.         if ($this->getJoin()) {
  1765.             // replace all 'column' by '$this->table.column' to prevent ambigious errors
  1766.             $metadata = $this->metadata();
  1767.             if (is_array($metadata)) {
  1768.                 foreach ($metadata as $aCol => $x) {
  1769.                     // handle ',id as xid,MAX(id),id' etc.
  1770. // FIXXME do this better!!!
  1771.                     $what = preg_replace(  "/(^|,|\()(\s*)$aCol(\)|\s|,|as|$)/i",
  1772.                                             // $2 is actually just to keep the spaces, is not really
  1773.                                             // necessary, but this way the test works independent of this functionality here
  1774.                                             "$1$2{$this->table}.$aCol$3",
  1775.                                             $what);
  1776.                 }
  1777.             }
  1778.             // replace all 'joinedTable.columnName' by '_joinedTable_columnName'
  1779.             // this actually only has an effect if there was no 'table.*' for 'table'
  1780.             // if that was there, then it has already been done before
  1781.             foreach ($this->getJoin('tables') as $aTable) {
  1782.                 if ($meta = $this->metadata($aTable)) {
  1783.                     foreach ($meta as $aCol=>$x) {
  1784.                         // dont put the 'AS' behind it if there is already one
  1785.                         if (preg_match("/$aTable.$aCol\s*as/i",$what)) {
  1786.                             continue;
  1787.                         }
  1788.                         // this covers a ' table.colName ' surrounded by spaces, and replaces it by ' table.colName AS _table_colName'
  1789.                         $what = preg_replace('/\s'.$aTable.'.'.$aCol.'\s/', " $aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol ", $what);
  1790.                         // replace also the column names which are behind a ','
  1791.                         // and do this also if the aCol is at the end that's what the $ is for
  1792.                         $what = preg_replace('/,\s*'.$aTable.'.'.$aCol.'(,|\s|$)/', ",$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol$1", $what);
  1793.                         // replace if colName is first and possibly also if at the beginning of the where-string
  1794.                         $what = preg_replace('/(^\s*|\s+)'.$aTable.'.'.$aCol.'\s*,/', "$1$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol,", $what);
  1795.                     }
  1796.                 }
  1797.             }
  1798.         }
  1799.         return $what;
  1800.     }
  1801.  
  1802.     // }}}
  1803.     // {{{ _buildWhere()
  1804.  
  1805.     /**
  1806.      *
  1807.      *
  1808.      * @version    2002/07/11
  1809.      * @access     public
  1810.      * @author     Wolfram Kriesing <wk@visionp.de>
  1811.      * @param      string where clause
  1812.      * @return
  1813.      */
  1814.     function _buildWhere($where='')
  1815.     {
  1816.         if ($this->getWhere()) {
  1817.             if (!empty($where)) {
  1818.                 $where = $this->getWhere().' AND '.$where;
  1819.             } else {
  1820.                 $where = $this->getWhere();
  1821.             }
  1822.         }
  1823.  
  1824.         if ($join = $this->getJoin()) {     // is join set?
  1825.             // only those where conditions in the default-join have to be added here
  1826.             // left-join conditions are added behind 'ON', the '_buildJoin()' does that
  1827.             if (@strlen($join['default']['where']) > 0) {
  1828.                 // we have to add this join-where clause here
  1829.                 // since at least in mysql a query like: select * from tableX JOIN tableY ON ...
  1830.                 // doesnt work, may be that's even SQL-standard...
  1831.                 if (trim($where)) {
  1832.                     $where = $join['default']['where'].' AND '.$where;
  1833.                 } else {
  1834.                     $where = $join['default']['where'];
  1835.                 }
  1836.             }
  1837.             // replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
  1838.             // since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
  1839. // FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
  1840.             $regExp = '/_('.implode('|', $this->getJoin('tables')).')_([^\s]+)/';
  1841.             $where = preg_replace($regExp, '$1.$2', $where);
  1842.             // add the table name before any column that has no table prefix
  1843.             // since this might cause "unambigious column" errors
  1844.             if ($meta = $this->metadata()) {
  1845.                 foreach ($meta as $aCol => $x) {
  1846.                     // this covers the LIKE,IN stuff: 'name LIKE "%you%"'  'id IN (2,3,4,5)'
  1847.                     $where = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $where);
  1848.                     // replace also the column names which are behind a '='
  1849.                     // and do this also if the aCol is at the end of the where clause
  1850.                     // that's what the $ is for
  1851.                     $where = preg_replace('/([=<>])\s*'.$aCol.'(\s|$)/', "$1{$this->table}.$aCol ", $where);
  1852.                     // replace if colName is first and possibly also if at the beginning of the where-string
  1853.                     $where = preg_replace('/(^\s*|\s+)'.$aCol.'\s*([=<>])/', "$1{$this->table}.$aCol$2", $where);
  1854.                 }
  1855.             }
  1856.         }
  1857.         return $where;
  1858.     }
  1859.  
  1860.     // }}}
  1861.     // {{{ _buildOrder()
  1862.  
  1863.     /**
  1864.      *
  1865.      *
  1866.      * @version    2002/07/11
  1867.      * @access     public
  1868.      * @author     Wolfram Kriesing <wk@visionp.de>
  1869.      * @param
  1870.      * @return
  1871.      */
  1872.     function _buildOrder()
  1873.     {
  1874.         $order = $this->getOrder();
  1875.         // replace 'column' by '$this->table.column' if the column is defined for $this->table
  1876.         if ($meta = $this->metadata()) {
  1877.             foreach ($meta as $aCol=>$x) {
  1878.                 $order = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $order);
  1879.             }
  1880.         }
  1881.         return $order;
  1882.     }
  1883.  
  1884.     // }}}
  1885.     // {{{ _buildGroup()
  1886.  
  1887.     /**
  1888.      *   Build the group-clause, replace 'column' by 'table.column'.
  1889.      *
  1890.      * @access public
  1891.      * @param void
  1892.      * @return string the rendered group clause
  1893.      */
  1894.     function _buildGroup()
  1895.     {
  1896.         $group = $this->getGroup();
  1897.         // replace 'column' by '$this->table.column' if the column is defined for $this->table
  1898.         if ($meta = $this->metadata()) {
  1899.             foreach ($meta as $aCol => $x) {
  1900.                 $group = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $group);
  1901.             }
  1902.         }
  1903.         return $group;
  1904.     }
  1905.  
  1906.     // }}}
  1907.     // {{{ _buildHaving()
  1908.  
  1909.     /**
  1910.      *
  1911.      * @version    2003/06/05
  1912.      * @access     public
  1913.      * @author     Johannes Schaefer <johnschaefer@gmx.de>
  1914.      * @param
  1915.      * @return string the having clause
  1916.      */
  1917.     function _buildHaving()
  1918.     {
  1919.         $having = $this->getHaving();
  1920.         // replace 'column' by '$this->table.column' if the column is defined for $this->table
  1921.         if ($meta = $this->metadata()) {
  1922.             foreach ($meta as $aCol => $x) {
  1923.                 $having = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U',"$1{$this->table}.$aCol$2",$having);
  1924.             }
  1925.         }
  1926.         return $having;
  1927.     }
  1928.  
  1929.     // }}}
  1930.     // {{{ _buildHaving()
  1931.  
  1932.     /**
  1933.      *
  1934.      * @version    2002/07/11
  1935.      * @access     public
  1936.      * @author     Wolfram Kriesing <wk@visionp.de>
  1937.      * @param      array   this array contains the elements of the query,
  1938.      *                       indexed by their key, which are: 'select','from','where', etc.
  1939.      * @return
  1940.      */
  1941.     function _buildSelectQuery($query=array())
  1942.     {
  1943. /*FIXXXME finish this
  1944.         $cacheKey = md5(serialize(????));
  1945.         if (isset($this->_queryCache[$cacheKey])) {
  1946.             $this->_errorLog('using cached query',__LINE__);
  1947.             return $this->_queryCache[$cacheKey];
  1948.         }
  1949. */
  1950.         $where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
  1951.         if ($where) {
  1952.             $where = 'WHERE '.$where;
  1953.         }
  1954.         $order = isset($query['order']) ? $query['order'] : $this->_buildOrder();
  1955.         if ($order) {
  1956.             $order = 'ORDER BY '.$order;
  1957.         }
  1958.         $group = isset($query['group']) ? $query['group'] : $this->_buildGroup();
  1959.         if ($group) {
  1960.             $group = 'GROUP BY '.$group;
  1961.         }
  1962.         $having = isset($query['having']) ? $query['having'] : $this->_buildHaving();
  1963.         if ($having) {
  1964.             $having = 'HAVING '.$having;
  1965.         }
  1966.         $queryString = sprintf( 'SELECT %s FROM %s %s %s %s %s',
  1967.                                 isset($query['select']) ? $query['select'] : $this->_buildSelect(),
  1968.                                 isset($query['from']) ? $query['from'] : $this->_buildFrom(),
  1969.                                 $where,
  1970.                                 $group,
  1971.                                 $order,
  1972.                                 $having
  1973.                                 );
  1974.         // $query['limit'] has preference!
  1975.         $limit = isset($query['limit']) ? $query['limit'] : $this->_limit;
  1976.         if (@$limit[1]) {    // is there a count set?
  1977.             $queryString=$this->db->modifyLimitQuery($queryString,$limit[0],$limit[1]);
  1978.             if (DB::isError($queryString)) {
  1979.                 $this->_errorSet('DB_QueryTool::db::modifyLimitQuery failed '.$queryString->getMessage());
  1980.                 $this->_errorLog($queryString->getUserInfo());
  1981.                 return false;
  1982.             }
  1983.         }
  1984. //        $this->_queryCache[$cacheKey] = $queryString;
  1985.         return $queryString;
  1986.     }
  1987.  
  1988.     // }}}
  1989.     // {{{ _buildUpdateQuery()
  1990.  
  1991.     /**
  1992.      * this simply builds an update query.
  1993.      *
  1994.      * @param  array   the parameter array might contain the following indexes
  1995.      *         'where'     the where clause to be added, i.e.
  1996.      *                     UPDATE table SET x=1 WHERE y=0
  1997.      *                     here the 'where' part simply would be 'y=0'
  1998.      *         'set'       the actual data to be updated
  1999.      *                     in the example above, that would be 'x=1'
  2000.      * @return string the resulting query
  2001.      */
  2002.     function _buildUpdateQuery($query=array())
  2003.     {
  2004.         $where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
  2005.         if ($where) {
  2006.             $where = 'WHERE '.$where;
  2007.         }
  2008.  
  2009.         $updateString = sprintf('UPDATE %s SET %s %s',
  2010.                                 $this->table,
  2011.                                 $query['set'],
  2012.                                 $where
  2013.                             );
  2014.         return $updateString;
  2015.     }
  2016.  
  2017.     // }}}
  2018.     // {{{ execute()
  2019.  
  2020.     /**
  2021.      *
  2022.      * @version    2002/07/11
  2023.      * @access     public
  2024.      * @author     Wolfram Kriesing <wk@visionp.de>
  2025.      * @param
  2026.      * @param      string  query method
  2027.      * @return     boolean
  2028.      */
  2029.     function execute($query=null, $method='getAll')
  2030.     {
  2031.         $this->writeLog();
  2032.         if (is_null($query)) {
  2033.             $query = $this->_buildSelectQuery();
  2034.         }
  2035.         $this->writeLog('query built: '.$query);
  2036.  
  2037. // FIXXME on ORACLE this doesnt work, since we return joined columns as _TABLE_COLNAME and the _ in front
  2038. // doesnt work on oracle, add a letter before it!!!
  2039.         $this->_lastQuery = $query;
  2040.  
  2041.         $this->debug($query);
  2042.         $this->writeLog('start query');
  2043.         if (DB::isError($res = $this->db->$method($query))) {
  2044.             $this->writeLog('end query (failed)');
  2045.             if ($this->getOption('verbose')) {
  2046.                 $this->_errorSet($res->getMessage());
  2047.             } else {
  2048.                 $this->_errorLog($res->getMessage());
  2049.             }
  2050.             $this->_errorLog($res->getUserInfo(), __LINE__);
  2051.             return false;
  2052.         } else {
  2053.             $this->writeLog('end query');
  2054.         }
  2055.         $res = $this->_makeIndexed($res);
  2056.         return $res;
  2057.     }
  2058.  
  2059.     // }}}
  2060.     // {{{ writeLog()
  2061.  
  2062.     /**
  2063.      *   Write events to the logfile.
  2064.      *
  2065.      *   It does some additional work, like time measuring etc. to
  2066.      *   see some additional info
  2067.      *
  2068.      */
  2069.     function writeLog($text='START')
  2070.     {
  2071. //its still really a quicky.... 'refactor' (nice word) that
  2072.         if (!isset($this->options['logfile'])) {
  2073.             return;
  2074.         }
  2075.  
  2076.         include_once 'Log.php';
  2077.         if (!class_exists('Log')) {
  2078.             return;
  2079.         }
  2080.         if (!$this->_logObject) {
  2081.             $this->_logObject =& Log::factory('file', $this->options['logfile']);
  2082.         }
  2083.  
  2084.         if ($text==='start query' || $text==='end query') {
  2085.             $bytesSent = $this->db->getAll("SHOW STATUS like 'Bytes_sent'");
  2086.             $bytesSent = $bytesSent[0]['Value'];
  2087.         }
  2088.         if ($text==='START') {
  2089.             $startTime = split(' ', microtime());
  2090.             $this->_logData['startTime'] = $startTime[1] + $startTime[0];
  2091.         }
  2092.         if ($text==='start query') {
  2093.             $this->_logData['startBytesSent'] = $bytesSent;
  2094.             $startTime = split(' ', microtime());
  2095.             $this->_logData['startQueryTime'] = $startTime[1] + $startTime[0];
  2096.             return;
  2097.         }
  2098.         if ($text==='end query') {
  2099.             $text .= ' result size: '.((int)$bytesSent-(int)$this->_logData['startBytesSent']).' bytes';
  2100.             $endTime = split(' ', microtime());
  2101.             $endTime = $endTime[1] + $endTime[0];
  2102.             $text .= ', took: '.(($endTime - $this->_logData['startQueryTime'])).' seconds';
  2103.         }
  2104.         if (strpos($text, 'query built')===0) {
  2105.             $endTime = split(' ', microtime());
  2106.             $endTime = $endTime[1] + $endTime[0];
  2107.             $this->writeLog('query building took: '.(($endTime - $this->_logData['startTime'])).' seconds');
  2108.         }
  2109.         $this->_logObject->log($text);
  2110.  
  2111.         if (strpos($text, 'end query')===0) {
  2112.             $endTime = split(' ', microtime());
  2113.             $endTime = $endTime[1] + $endTime[0];
  2114.             $text = 'time over all: '.(($endTime - $this->_logData['startTime'])).' seconds';
  2115.             $this->_logObject->log($text);
  2116.         }
  2117.     }
  2118.  
  2119.     // }}}
  2120.     // {{{ returnResult()
  2121.  
  2122.     /**
  2123.      * Return the chosen result type
  2124.      *
  2125.      * @version    2004/04/28
  2126.      * @access     public
  2127.      * @param object reference
  2128.      * @return mixed
  2129.      */
  2130.     function returnResult(&$result)
  2131.     {
  2132.         if ($this->_resultType == 'none') {
  2133.             return $result;
  2134.         }
  2135.         if ($result === false) {
  2136.             return false;
  2137.         }
  2138.         //what about allowing other (custom) result types?
  2139.         switch (strtolower($this->_resultType)) {
  2140.             case 'object':  return new DB_QueryTool_Result_Object($result);
  2141.             case 'array':
  2142.             default:        return new DB_QueryTool_Result($result);
  2143.         }
  2144.     }
  2145.  
  2146.     // }}}
  2147.     // {{{ _makeIndexed()
  2148.  
  2149.     /**
  2150.      *
  2151.      * @version    2002/07/11
  2152.      * @access     public
  2153.      * @author     Wolfram Kriesing <wk@visionp.de>
  2154.      * @param      mixed
  2155.      * @return     mixed
  2156.      */
  2157.     function &_makeIndexed(&$data)
  2158.     {
  2159.         // we can only return an indexed result if the result has a number of columns
  2160.         if (is_array($data) && sizeof($data) && $key = $this->getIndex()) {
  2161.             // build the string to evaluate which might be made up out of multiple indexes of a result-row
  2162.             $evalString = '$val[\''.implode('\'].\',\'.$val[\'',explode(',',$key)).'\']';   //"
  2163.  
  2164.             $indexedData = array();
  2165. //FIXXME actually we also need to check ONCE if $val is an array, so to say if $data is 2-dimensional
  2166.             foreach ($data as $val) {
  2167.                 eval("\$keyValue = $evalString;");  // get the actual real (string-)key (string if multiple cols are used as index)
  2168.                 $indexedData[$keyValue] = $val;
  2169.             }
  2170.             unset($data);
  2171.             return $indexedData;
  2172.         }
  2173.  
  2174.         return $data;
  2175.     }
  2176.  
  2177.     // }}}
  2178.     // {{{ setIndex()
  2179.  
  2180.     /**
  2181.      * format the result to be indexed by $key
  2182.      * NOTE: be careful, when using this you should be aware, that if you
  2183.      * use an index which's value appears multiple times you may loose data
  2184.      * since a key cant exist multiple times!!
  2185.      * the result for a result to be indexed by a key(=columnName)
  2186.      * (i.e. 'relationtoMe') which's values are 'brother' and 'sister'
  2187.      * or alike normally returns this:
  2188.      *     $res['brother'] = array('name'=>'xxx')
  2189.      *     $res['sister'] = array('name'=>'xxx')
  2190.      * but if the column 'relationtoMe' contains multiple entries for 'brother'
  2191.      * then the returned dataset will only contain one brother, since the
  2192.      * value from the column 'relationtoMe' is used
  2193.      * and which 'brother' you get depends on a lot of things, like the sortorder,
  2194.      * how the db saves the data, and whatever else
  2195.      *
  2196.      * you can also set indexes which depend on 2 columns, simply pass the parameters like
  2197.      * 'table1.id,table2.id' it will be used as a string for indexing the result
  2198.      * and the index will be built using the 2 values given, so a possible
  2199.      * index might be '1,2' or '2108,29389' this way you can access data which
  2200.      * have 2 primary keys. Be sure to remember that the index is a string!
  2201.      *
  2202.      * @version    2002/07/11
  2203.      * @access     public
  2204.      * @author     Wolfram Kriesing <wk@visionp.de>
  2205.      * @param
  2206.      * @return
  2207.      */
  2208.     function setIndex($key=null)
  2209.     {
  2210.         if ($this->getJoin()) { // is join set?
  2211.             // replace TABLENAME.COLUMNNAME by _TABLENAME_COLUMNNAME
  2212.             // since this is only the result-keys can be used for indexing :-)
  2213.             $regExp = '/('.implode('|', $this->getJoin('tables')).')\.([^\s]+)/';
  2214.             $key = preg_replace($regExp, '_$1_$2', $key);
  2215.  
  2216.             // remove the table name if it is in front of '<$this->table>.columnname'
  2217.             // since the key doesnt contain it neither
  2218.             if ($meta = $this->metadata()) {
  2219.                 foreach ($meta as $aCol => $x) {
  2220.                     $key = preg_replace('/'.$this->table.'\.'.$aCol.'/', $aCol, $key);
  2221.                 }
  2222.             }
  2223.         }
  2224.         $this->_index = $key;
  2225.     }
  2226.  
  2227.     // }}}
  2228.     // {{{ getIndex()
  2229.  
  2230.     /**
  2231.      *
  2232.      * @version    2002/07/11
  2233.      * @access     public
  2234.      * @author     Wolfram Kriesing <wk@visionp.de>
  2235.      * @param
  2236.      * @return
  2237.      */
  2238.     function getIndex()
  2239.     {
  2240.         return $this->_index;
  2241.     }
  2242.  
  2243.     // }}}
  2244.     // {{{ useResult()
  2245.  
  2246.     /**
  2247.      * Choose the type of the returned result
  2248.      *
  2249.      * @version    2004/04/28
  2250.      * @access     public
  2251.      * @param string $type  ['array' | 'object' | 'none']
  2252.      *             For BC reasons, $type=true is equal to 'array',
  2253.      *             $type=false is equal to 'none'
  2254.      */
  2255.     function useResult($type='array')
  2256.     {
  2257.         if ($type === true) {
  2258.             $type = 'array';
  2259.         } elseif ($type === false) {
  2260.             $type = 'none';
  2261.         }
  2262.         switch (strtolower($type)) {
  2263.             case 'array':
  2264.                 $this->_resultType = 'array';
  2265.                 require_once 'DB/QueryTool/Result.php';
  2266.                 break;
  2267.             case 'object':
  2268.                 $this->_resultType = 'object';
  2269.                 require_once 'DB/QueryTool/Result/Object.php';
  2270.                 break;
  2271.             default:
  2272.                 $this->_resultType = 'none';
  2273.         }
  2274.     }
  2275.  
  2276.     // }}}
  2277.     // {{{ setErrorCallback()
  2278.  
  2279.     /**
  2280.      * set both callbacks
  2281.      * @param string
  2282.      */
  2283.     function setErrorCallback($param='')
  2284.     {
  2285.         $this->setErrorLogCallback($param);
  2286.         $this->setErrorSetCallback($param);
  2287.     }
  2288.  
  2289.     // }}}
  2290.     // {{{ setErrorLogCallback()
  2291.  
  2292.     /**
  2293.      * @param string
  2294.      */
  2295.     function setErrorLogCallback($param='')
  2296.     {
  2297.         $errorLogCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorLogCallback');
  2298.         $errorLogCallback = $param;
  2299.     }
  2300.  
  2301.     // }}}
  2302.     // {{{ setErrorSetCallback()
  2303.  
  2304.     /**
  2305.      * @param string
  2306.      */
  2307.     function setErrorSetCallback($param='')
  2308.     {
  2309.         $errorSetCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorSetCallback');
  2310.         $errorSetCallback = $param;
  2311.     }
  2312.  
  2313.     // }}}
  2314.     // {{{ _errorLog()
  2315.  
  2316.     /**
  2317.      * sets error log and adds additional info
  2318.      *
  2319.      * @version    2002/04/16
  2320.      * @access     public
  2321.      * @author     Wolfram Kriesing <wk@visionp.de>
  2322.      * @param      string  the actual message, first word should always be the method name,
  2323.      *                     to build the message like this: className::methodname
  2324.      * @param      integer the line number
  2325.      */
  2326.     function _errorLog($msg, $line='unknown')
  2327.     {
  2328.         $this->_errorHandler('log', $msg, $line);
  2329. /*
  2330.         if ($this->getOption('verbose') == true)
  2331.         {
  2332.             $this->_errorLog(get_class($this)."::$msg ($line)");
  2333.             return;
  2334.         }
  2335.  
  2336.         if ($this->_errorLogCallback)
  2337.             call_user_func($this->_errorLogCallback, $msg);
  2338. */
  2339.     }
  2340.  
  2341.     // }}}
  2342.     // {{{ _errorSet()
  2343.  
  2344.     /**
  2345.      * @param      string
  2346.      * @param      string
  2347.      */
  2348.     function _errorSet($msg, $line='unknown')
  2349.     {
  2350.         $this->_errorHandler('set', $msg, $line);
  2351.     }
  2352.  
  2353.     // }}}
  2354.     // {{{ _errorHandler()
  2355.  
  2356.     /**
  2357.      * @param
  2358.      * @param      string
  2359.      * @param      string
  2360.      */
  2361.     function _errorHandler($logOrSet, $msg, $line='unknown')
  2362.     {
  2363. /* what did i do this for?
  2364.         if ($this->getOption('verbose') == true)
  2365.         {
  2366.             $this->_errorHandler($logOrSet, get_class($this)."::$msg ($line)");
  2367.             return;
  2368.         }
  2369. */
  2370.  
  2371.         $msg = get_class($this)."::$msg ($line)";
  2372.  
  2373.         $logOrSet = ucfirst($logOrSet);
  2374.         $callback = &PEAR::getStaticProperty('DB_QueryTool','_error'.$logOrSet.'Callback');
  2375.         if ($callback)
  2376.             call_user_func($callback, $msg);
  2377. //        else
  2378. //          ?????
  2379.  
  2380.     }
  2381.  
  2382.     // }}}
  2383. }
  2384. ?>